# -*- coding: utf-8 -*-
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

"""类型构造函数发射器

处理类型构造函数调用，如 set(), list(), dict(), int(), float(), str(), bool() 等。
这些不是全局函数，而是类型的构造函数。

关键设计：
- 通过符号表查找，判断是类型还是函数
- 使用类型配置中的 cpp_type、includes 等信息
- 处理泛型参数替换
"""

from mcpy.ir.expressions import IRCall
from mcpy.ir.expressions.basic import IRName
from mcpy.ir.expressions.access import IRSubscript
from mcpy.ir.expressions.literals import IRTuple
from mcpy.symbol_meta import SymbolKind
from mcpy.type_system import substitute_template_params_with_config
from ..core.emitter import EmitResult, NodeTypeEmitter
from ..core.context import CodeGenContext
from ..configs.operators import get_operator_precedence
from ..core.generator import get_global_generator


class TypeConstructorEmitter(NodeTypeEmitter):
    """处理类型构造函数的发射器

    将 set(), list[int](), int() 等调用理解为类型构造，而不是全局函数调用。

    工作流程：
    1. 检查调用的函数名是否是类型符号（通过符号表）
    2. 从类型的 type_config 中获取 cpp_type
    3. 处理泛型参数替换（如果有）
    4. 生成构造代码

    支持的模式：
    - set() -> mcpy::set<mc::variant>()
    - set[int]() -> mcpy::set<int>()
    - list() -> mc::variants{}
    - list[Any]() -> mc::variants{}
    - list[str]() -> mc::array<std::string>{}
    - int() -> int{}
    - int("123") -> int("123") 或需要转换函数
    """

    def __init__(self):
        super().__init__([IRCall])

    def emit(self, node: IRCall, context: CodeGenContext) -> EmitResult | None:
        """生成类型构造代码

        支持通过 type_config 中的 constructors 配置来匹配和生成构造函数代码。

        优先级：
        1. 如果参数类型有对应的转换方法（如 __bool__, __int__, __str__），
           优先使用转换方法而不是构造函数
        2. 否则使用类型构造函数
        """
        # 获取参数类型（用于符号查找和签名匹配）
        args_types = self._get_args_types(node, context)

        # 查找类型符号（传递参数类型）
        type_symbol = self._get_type_symbol(node, context, args_types)

        # 如果不是类型符号，返回 None 让其他 emitter 处理
        if not type_symbol:
            return None

        # 只处理真正的类型（SymbolKind.CLASS），不处理变量
        if type_symbol.kind != SymbolKind.CLASS:
            return None

        # 如果是异常类型，返回 None 让 ExceptionConstructorEmitter 处理
        if self._is_exception_type(type_symbol):
            return None

        # 通用机制：检查参数是否有对应的类型转换方法
        # 例如：bool(x) 检查 x.__bool__，int(x) 检查 x.__int__
        conversion_result = self._try_emit_type_conversion(node, type_symbol, context)
        if conversion_result:
            return conversion_result

        if node.args and type_symbol.signatures:
            # 使用 Symbol 的 find_matching_signature 方法进行签名匹配
            matching_sig = type_symbol.find_matching_signature(args_types)

            if matching_sig:
                return self._emit_with_constructor_signature(
                    node, type_symbol, matching_sig, context
                )

        # 无参数的默认构造函数
        type_config = type_symbol.type_config or {}

        # 获取 C++ 类型
        cpp_type = self._get_cpp_type(node, type_symbol, context)

        # 处理参数（当前为空）
        args = self._render_args(node, context)

        # 生成构造代码
        code = self._generate_constructor_code(
            type_symbol.name, cpp_type, args, type_config, node, context
        )

        # 添加需要的头文件
        includes = type_config.get("includes", [])
        for include in includes:
            context.require_include(include)

        return EmitResult(code=code, precedence=get_operator_precedence("call"))

    def _get_args_types(self, node: IRCall, context: CodeGenContext):
        """获取参数的类型信息

        Args:
            node: IRCall 节点
            context: 代码生成上下文

        Returns:
            参数类型列表
        """
        args_types = []
        for arg in node.args:
            arg_type = arg.get_extension_param("inferred_type")
            if arg_type:
                args_types.append(arg_type)
        return args_types

    def _get_type_symbol(self, node: IRCall, context: CodeGenContext, args_types=None):
        """获取类型符号

        支持：
        - set() -> 查找 "set" 类型
        - set[int]() -> 查找 "set" 类型

        使用作用域查询而不是直接查全局符号表

        Args:
            node: IRCall 节点
            context: 代码生成上下文
            args_types: 参数类型列表（不用于类型查找，仅用于后续签名匹配）

        注意：对于类型查找，不传递 args_types，避免被误当作泛型类型参数
        """
        # 确保有当前节点上下文用于作用域查询
        if not context.current_node:
            return None

        type_name = None
        if isinstance(node.func, IRName):
            type_name = node.func.id
        elif isinstance(node.func, IRSubscript):
            # set[int]() 这种形式
            if isinstance(node.func.value, IRName):
                type_name = node.func.value.id

        if not type_name:
            return None

        # 使用作用域查询查找类型符号
        # 重要：不传递 args_types，因为 args_types 是构造函数参数类型，
        # 不是泛型类型参数，避免 lookup_symbol 误将其当作泛型实例化
        # TypeVar 被注册为 VARIABLE，所以需要同时查找 CLASS 和 VARIABLE
        type_symbol = context.current_node.lookup_symbol(
            type_name,
            symbol_kinds=[SymbolKind.CLASS, SymbolKind.VARIABLE],
        )

        # 如果找不到符号或不是类型，返回 None
        if not type_symbol:
            return None

        # 确认有 type_config（说明是类型，不是普通变量）
        if not hasattr(type_symbol, "type_config") or not type_symbol.type_config:
            return None

        return type_symbol

    def _get_cpp_type(self, node: IRCall, type_symbol, context: CodeGenContext) -> str:
        """获取 C++ 类型字符串

        处理泛型参数替换，优先级：
        1. 从类型推导获取的完整类型（支持期望类型推导）
        2. 显式类型参数 set[int]()
        3. 默认类型 set() -> set<mc::variant>
        """
        type_config = type_symbol.type_config or {}
        cpp_type_template = type_config.get("cpp_type", "")

        if not cpp_type_template:
            return type_symbol.name

        # 优先使用类型推导的结果（支持期望类型推导）
        # 例如：s: set[int] = set() 时，类型推导会返回 set[int]
        inferred_cpp_type = node.get_extension_param("cpp_type")
        if inferred_cpp_type and "<" in inferred_cpp_type:
            # 如果推导出了完整的泛型类型，直接使用
            return inferred_cpp_type

        # 检查是否有显式类型参数 set[int]()
        type_params = self._extract_type_parameters(node, context)

        if type_params:
            # 替换类型参数
            # 例如：mcpy::set<T> -> mcpy::set<int>
            # 使用统一的模板替换函数（来自 type_system）
            cpp_type = substitute_template_params_with_config(
                cpp_type_template, type_params, type_config
            )
        else:
            # 使用默认类型或保持模板形式
            default_type = type_config.get("default_type")
            if default_type:
                # 如果有默认类型，用默认类型替换
                cpp_type = substitute_template_params_with_config(
                    cpp_type_template, [default_type], type_config
                )
            else:
                # 没有默认类型，保持原样
                cpp_type = cpp_type_template

        return cpp_type

    def _extract_type_parameters(self, node: IRCall, context: CodeGenContext) -> list:
        """提取类型参数

        set[int]() -> ["int"]
        dict[str, int]() -> ["std::string", "int"]
        set() -> []
        """
        if not isinstance(node.func, IRSubscript):
            return []

        generator = get_global_generator()

        # 提取下标中的类型表达式
        slice_node = node.func.slice

        # 可能是单个类型或元组类型
        if isinstance(slice_node, IRTuple):
            # dict[str, int] 这种多参数形式
            type_params = []
            for elem in slice_node.elements:
                cpp_param = elem.get_extension_param("cpp_type")
                if not cpp_param:
                    # 类型推导失败，生成原样代码
                    cpp_param = generator.generate_expression(elem, context)
                type_params.append(cpp_param)
            return type_params
        else:
            # set[int] 这种单参数形式
            cpp_param = slice_node.get_extension_param("cpp_type")
            if not cpp_param:
                # 类型推导失败，生成原样代码
                cpp_param = generator.generate_expression(slice_node, context)
            return [cpp_param]

    def _generate_constructor_code(
        self,
        type_name: str,
        cpp_type: str,
        args: list,
        type_config: dict,
        node: IRCall,
        context: CodeGenContext,
    ) -> str:
        """生成构造代码

        使用配置决定构造语法：
        - default_constructor_syntax: "parens" | "braces" (默认 "braces")

        说明：
        - 默认构造（无参数）：根据 default_constructor_syntax 生成 type() 或 type{}
        - 带参数构造：统一使用 type(args) 形式

        注意：带参数的类型转换（如 int("123")）由 type_conversion 机制处理，
              不在此处生成。
        """

        if not args:
            # 默认构造
            # 从配置读取构造语法
            syntax = type_config.get("default_constructor_syntax", "braces")

            if syntax == "parens":
                # 函数调用形式：type()
                return f"{cpp_type}()"
            else:
                # 花括号初始化：type{}
                return f"{cpp_type}{{}}"
        else:
            # 带参数的构造
            # 统一使用括号形式：type(args)
            return f"{cpp_type}({', '.join(args)})"

    def _render_args(self, node: IRCall, context: CodeGenContext) -> list:
        """渲染参数列表"""
        generator = get_global_generator()
        args = []

        for arg in node.args:
            arg_code = generator.generate_expression(arg, context)
            args.append(arg_code)

        return args

    def _emit_with_constructor_signature(
        self, node: IRCall, type_symbol, matching_sig, context: CodeGenContext
    ) -> EmitResult | None:
        """使用构造函数签名生成代码

        Args:
            node: IRCall 节点
            type_symbol: 类型符号（包含构造函数签名）
            matching_sig: 已匹配的签名
            context: 代码生成上下文

        Returns:
            生成结果
        """
        from mcpy.template_system import replace_template_params
        from mcpy.ir.expressions.literals import IRList, IRSet, IRDict, IRTuple

        # 获取节点的 C++ 类型（已实例化，如 mc::array<float>）
        node_cpp_type = context.get_cpp_type(node)

        # 检查是否需要使用显式类型参数的 list_construct<ElementType>
        #
        # 代码生成规则说明：
        # 1. **显式类型场景**（如 list[int]([5, 6])）：
        #    - 判断依据：func 是 IRSubscript（用户在Python代码中显式写了 list[int]）
        #    - 使用：list_construct<int>(...)  （显式类型参数）
        #    - 理由：保留Python层面的显式类型信息，不依赖C++类型推导
        #
        # 2. **隐式类型场景**（如 list(source)）：
        #    - 判断依据：func 是 IRName（用户只写了 list，没有类型注解）
        #    - 使用：list_construct(...)  （无类型参数，依赖 Container::value_type）
        #    - 理由：依赖C++的类型推导，如果参数类型匹配则可以正确推导
        #
        # 为什么隐式类型不使用显式版本？
        # - 隐式类型场景下，类型是从上下文推导的（如函数返回类型）
        # - 如果参数类型已经匹配（如 source 是 mc::array<int>），C++推导即可
        # - 如果需要类型转换（如 source 是 mc::variants，但返回类型是 list[int]），
        #   理论上应该使用 list_construct<int>，但当前设计依赖类型系统确保类型匹配
        #
        # 条件：1）显式指定了类型（从 IRSubscript 提取）
        #       2）模板中使用的是 list_construct
        #       3）节点类型是 mc::array<ElementType>
        use_explicit_type_construct = False
        element_cpp_type = None
        if node_cpp_type and node_cpp_type.startswith("mc::array<") and node.args:
            # 提取元素类型（如 mc::array<float> -> float）
            import re

            match = re.search(r"<(.+?)>", node_cpp_type)
            if match:
                element_cpp_type = match.group(1)

            # 检查是否是显式类型调用（func 是 IRSubscript，如 list[float]）
            from mcpy.ir.expressions.access import IRSubscript

            if isinstance(node.func, IRSubscript):
                # 检查模板是否使用 list_construct
                if "list_construct" in matching_sig.emit_template:
                    use_explicit_type_construct = True

        if use_explicit_type_construct and element_cpp_type:
            # 显式指定了类型（如 list[float]），统一使用 list_construct<ElementType>
            # 这样可以：
            # 1. 保留Python层面的显式类型信息（不依赖C++类型推导）
            # 2. 保持代码生成的一致性（所有显式类型构造都使用相同的模式）
            # 3. 便于类型转换（如果参数类型与目标类型不同）
            generator = get_global_generator()
            arg = node.args[0]
            # 为参数创建上下文，设置期望类型以便容器发射器正确生成代码
            # 如果参数是 IRList，需要设置正确的 expected_cpp_type 以便生成显式类型
            from mcpy.ir.expressions.literals import IRList, IRDict, IRSet, IRTuple
            if isinstance(arg, (IRList, IRDict, IRSet, IRTuple)):
                # 对于容器参数，设置期望类型为节点的 cpp_type，以便容器发射器知道正确的类型
                arg_context = context.create_child_context(
                    expected_cpp_type=node_cpp_type
                )
                arg_code = generator.generate_expression(arg, arg_context)
            else:
                arg_code = generator.generate_expression(arg, context)

            # 统一使用显式类型的 list_construct<ElementType>
            code = f"mcpy::list_construct<{element_cpp_type}>({arg_code})"

            # 添加头文件
            for include in matching_sig.includes:
                context.require_include(include)

            return EmitResult(code=code, precedence=get_operator_precedence("call"))

        # 渲染参数
        args = self._render_args(node, context)

        # 获取模板代码
        code = matching_sig.emit_template

        # 先替换类型参数（如 T, K, V）
        # 从节点的 inferred_type 获取具体的类型参数
        inferred_type = node.get_extension_param("inferred_type")
        if inferred_type and hasattr(inferred_type, "args") and inferred_type.args:
            # 使用 TemplateEmitter 的逻辑替换类型参数
            from mcpy.codegen.emitters.template_emitter import TemplateEmitter

            emitter = TemplateEmitter()
            code = emitter._replace_type_parameters(code, inferred_type, context)

        # 再使用 replace_template_params 替换参数占位符
        code = replace_template_params(code, matching_sig, args)

        # 添加头文件
        for include in matching_sig.includes:
            context.require_include(include)

        return EmitResult(code=code, precedence=get_operator_precedence("call"))

    def _try_emit_type_conversion(
        self, node: IRCall, type_symbol, context: CodeGenContext
    ) -> EmitResult | None:
        """尝试生成类型转换方法调用代码

        通用类型转换机制：
        - bool(x) → 调用 x.__bool__()
        - int(x) → 调用 x.__int__()
        - float(x) → 调用 x.__float__()
        - str(x) → 调用 x.__str__()

        Args:
            node: 调用节点
            type_symbol: 类型符号（如 bool, int, str）
            context: 代码生成上下文

        Returns:
            EmitResult: 如果找到转换方法则返回生成的代码，否则返回 None
        """
        # 只对单参数调用检查
        if not node.args or len(node.args) != 1:
            return None

        # 获取类型名，构造转换方法名
        type_name = type_symbol.name
        conversion_method_name = f"__{type_name}__"

        # 获取参数的类型信息
        arg = node.args[0]
        arg_type = arg.get_extension_param("inferred_type")
        if not arg_type:
            return None

        # 查找转换方法
        if not context.current_node:
            return None

        method_symbol = context.current_node.lookup_symbol(
            conversion_method_name,
            func_args=[],
            owner_type=arg_type,
        )

        # 如果没有找到转换方法，返回 None
        if not method_symbol:
            return None

        # 找到了转换方法，获取匹配的签名
        matching_signature = method_symbol.find_matching_signature([arg_type])
        if not matching_signature and method_symbol.signatures:
            matching_signature = method_symbol.signatures[0]

        if not matching_signature or not matching_signature.emit_template:
            return None

        # 生成参数代码
        from mcpy.codegen.core.generator import get_global_generator

        generator = get_global_generator()
        arg_code = generator.generate_expression(arg, context, parent_precedence=100)

        # 应用模板生成代码
        code = matching_signature.emit_template.replace("{self}", arg_code)

        # 添加头文件
        for include in matching_signature.includes:
            context.require_include(include)

        return EmitResult(code=code, precedence=get_operator_precedence("call"))

    def _is_exception_type(self, symbol) -> bool:
        """检查符号是否是异常类型

        异常类型应该由 ExceptionConstructorEmitter 处理，不应该在这里处理。
        通过检查继承链判断，只有继承自 Exception 或 BaseException 的类型才是异常类型。
        """
        if not symbol.type_config:
            return False

        # 检查 cpp_type 是否直接是 mc::exception
        cpp_type = symbol.type_config.get("cpp_type", "")
        if cpp_type == "mc::exception":
            return True

        # 检查 inherit_from（递归）
        inherit_from = symbol.type_config.get("inherit_from")
        if inherit_from:
            if inherit_from in ("Exception", "BaseException"):
                return True
            # 注意：这里简化处理，不递归查找，因为 TypeConstructorEmitter
            # 只需要快速判断是否需要跳过，详细判断由 ExceptionConstructorEmitter 负责

        # 检查 base_classes（向后兼容）
        base_classes = symbol.type_config.get("base_classes", [])
        for base_class in base_classes:
            if base_class in ("Exception", "BaseException"):
                return True

        return False

    def get_priority(self) -> int:
        """获取发射器优先级

        数字越大优先级越高（索引系统使用 reverse=True 排序）
        需要在 CallEmitter 之前处理类型构造
        """
        return 55  # 比 CallEmitter(50) 更高，优先处理类型构造
